// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "cc/output/overlay_processor.h"

#include "cc/output/output_surface.h"
#include "cc/output/overlay_strategy_single_on_top.h"
#include "cc/output/overlay_strategy_underlay.h"
#include "cc/quads/draw_quad.h"
#include "cc/resources/resource_provider.h"
#include "ui/gfx/geometry/rect_conversions.h"
#include "ui/gfx/transform.h"

namespace {

#if defined(OS_ANDROID)
// Utility class to make sure that we notify resource that they're promotable
// before returning from ProcessForOverlays.
class SendPromotionHintsBeforeReturning {
public:
    SendPromotionHintsBeforeReturning(cc::ResourceProvider* resource_provider,
        cc::OverlayCandidateList* candidates)
        : resource_provider_(resource_provider)
        , candidates_(candidates)
    {
    }
    ~SendPromotionHintsBeforeReturning()
    {
        resource_provider_->SendPromotionHints(
            candidates_->promotion_hint_info_map_);
    }

private:
    cc::ResourceProvider* resource_provider_;
    cc::OverlayCandidateList* candidates_;

    DISALLOW_COPY_AND_ASSIGN(SendPromotionHintsBeforeReturning);
};
#endif

} // namespace

namespace cc {

OverlayProcessor::OverlayProcessor(OutputSurface* surface)
    : surface_(surface)
{
}

void OverlayProcessor::Initialize()
{
    DCHECK(surface_);
    OverlayCandidateValidator* validator = surface_->GetOverlayCandidateValidator();
    if (validator)
        validator->GetStrategies(&strategies_);
}

OverlayProcessor::~OverlayProcessor() { }

gfx::Rect OverlayProcessor::GetAndResetOverlayDamage()
{
    gfx::Rect result = overlay_damage_rect_;
    overlay_damage_rect_ = gfx::Rect();
    return result;
}

bool OverlayProcessor::ProcessForCALayers(
    ResourceProvider* resource_provider,
    RenderPass* render_pass,
    const RenderPassFilterList& render_pass_filters,
    const RenderPassFilterList& render_pass_background_filters,
    OverlayCandidateList* overlay_candidates,
    CALayerOverlayList* ca_layer_overlays,
    gfx::Rect* damage_rect)
{
    OverlayCandidateValidator* overlay_validator = surface_->GetOverlayCandidateValidator();
    if (!overlay_validator || !overlay_validator->AllowCALayerOverlays())
        return false;

    //   if (!ProcessForCALayerOverlays(
    //           resource_provider, gfx::RectF(render_pass->output_rect),
    //           render_pass->quad_list, render_pass_filters,
    //           render_pass_background_filters, ca_layer_overlays))
    //     return false;
    DebugBreak();

    // CALayer overlays are all-or-nothing. If all quads were replaced with
    // layers then clear the list and remove the backbuffer from the overcandidate
    // list.
    overlay_candidates->clear();
    overlay_damage_rect_ = render_pass->output_rect;
    *damage_rect = gfx::Rect();
    return true;
}

void OverlayProcessor::ProcessForOverlays(
    ResourceProvider* resource_provider,
    RenderPass* render_pass,
    const RenderPassFilterList& render_pass_filters,
    const RenderPassFilterList& render_pass_background_filters,
    OverlayCandidateList* candidates,
    CALayerOverlayList* ca_layer_overlays,
    gfx::Rect* damage_rect)
{
#if defined(OS_ANDROID)
    // Be sure to send out notifications, regardless of whether we get to
    // processing for overlays or not.  If we don't, then we should notify that
    // they are not promotable.
    SendPromotionHintsBeforeReturning notifier(resource_provider, candidates);
#endif

    // If we have any copy requests, we can't remove any quads for overlays or
    // CALayers because the framebuffer would be missing the removed quads'
    // contents.
    if (!render_pass->copy_requests.empty()) {
        // If overlay processing was skipped for a frame there's no way to be sure
        // of the state of the previous frame, so reset.
        previous_frame_underlay_rect_ = gfx::Rect();
        return;
    }

    // First attempt to process for CALayers.
    if (ProcessForCALayers(resource_provider, render_pass, render_pass_filters,
            render_pass_background_filters, candidates,
            ca_layer_overlays, damage_rect)) {
        return;
    }

    // Only if that fails, attempt hardware overlay strategies.
    for (const auto& strategy : strategies_) {
        if (!strategy->Attempt(resource_provider, render_pass, candidates))
            continue;

        UpdateDamageRect(candidates, damage_rect);
        return;
    }
}

// Subtract on-top overlays from the damage rect, unless the overlays use
// the backbuffer as their content (in which case, add their combined rect
// back to the damage at the end).
// Also subtract unoccluded underlays from the damage rect if we know that the
// same underlay was scheduled on the previous frame. If the renderer decides
// not to swap the framebuffer there will still be a transparent hole in the
// previous frame. This only handles the common case of a single underlay quad
// for fullscreen video.
void OverlayProcessor::UpdateDamageRect(OverlayCandidateList* candidates,
    gfx::Rect* damage_rect)
{
    gfx::Rect output_surface_overlay_damage_rect;
    gfx::Rect this_frame_underlay_rect;
    for (const OverlayCandidate& overlay : *candidates) {
        if (overlay.plane_z_order > 0) {
            const gfx::Rect overlay_display_rect = ToEnclosedRect(overlay.display_rect);
            overlay_damage_rect_.Union(overlay_display_rect);
            damage_rect->Subtract(overlay_display_rect);
            if (overlay.use_output_surface_for_resource)
                output_surface_overlay_damage_rect.Union(overlay_display_rect);
        } else if (overlay.plane_z_order < 0 && overlay.is_unoccluded && this_frame_underlay_rect.IsEmpty()) {
            this_frame_underlay_rect = ToEnclosedRect(overlay.display_rect);
        }
    }

    if (this_frame_underlay_rect == previous_frame_underlay_rect_)
        damage_rect->Subtract(this_frame_underlay_rect);
    previous_frame_underlay_rect_ = this_frame_underlay_rect;

    damage_rect->Union(output_surface_overlay_damage_rect);
}

} // namespace cc
